package com.czk.zookeeper.zookeepercli.lock.practice;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 用zookeeper来实现分布式锁的复习
 *
 * @Author:ChenZhangKun
 * @Date: 2021/7/27 21:27
 */
public class PracticeDemo {
    String IP = "192.168.25.133:2181";
    CountDownLatch countDownLatch = new CountDownLatch(1);
    ZooKeeper zooKeeper;
    /**
     * 根节点
     */
    private static final String LOCK_ROOT_PATH = "/locks";
    private static final String LOCK_NODE_NAME = "Lock_";
    private String lockPath;
    private final Watcher watcher = new Watcher() {
        @Override
        public void process(WatchedEvent watchedEvent) {
            // 是否是删除 释放锁是删除
            if (watchedEvent.getType() == Event.EventType.NodeDeleted) {
                // 唤醒下一个节点
                notify();
            }
        }
    };

    public PracticeDemo() throws IOException, InterruptedException {
        // 连接
        zooKeeper = new ZooKeeper(IP, 5000, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                    // 连接上了
                    System.out.println(watchedEvent.getType());
                    countDownLatch.countDown();
                }
            }
        });
        // 阻塞
        countDownLatch.await();
    }

    /**
     * 创建锁
     *
     * @throws KeeperException
     * @throws InterruptedException
     */
    public void createLock() throws KeeperException, InterruptedException {
        // 查看主节点是否存在
        Stat stat = zooKeeper.exists(LOCK_ROOT_PATH, null);
        if (stat == null) {
            // 创建节点(持久性)
            zooKeeper.create(LOCK_ROOT_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        }
        // 创建子节点
        // 临时有序节点
        lockPath = zooKeeper.create(LOCK_ROOT_PATH + "/" + LOCK_NODE_NAME, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println("得到的节点路径是" + lockPath);
    }

    /**
     * 尝试获取锁
     *
     * @throws KeeperException
     * @throws InterruptedException
     */
    public void attemptLock() throws KeeperException, InterruptedException {
        // 拿到所有的子节点
        List<String> children = zooKeeper.getChildren(LOCK_ROOT_PATH, false);
        // 排序
        Collections.sort(children);
        // 查看自己的索引
        int i = children.indexOf(lockPath.substring(LOCK_NODE_NAME.length() + 1));
        // 第一个拿到锁
        if (i == 0) {
            // 获取锁了
            System.out.println("获取锁");
            // 做业务
            TimeUnit.SECONDS.sleep(1);
            // 释放锁
            release();
        } else if (i > 0) {
            // 拿到自己的上一个节点
            String prePath = children.get(i - 1);
            // 再次尝试是否存在
            Stat stat = zooKeeper.exists(LOCK_ROOT_PATH + "/" + prePath, watcher);
            //
            if (stat == null) {
                // 再次尝试获取锁
                attemptLock();
            } else {
                // 等待
                synchronized (watcher) {
                    watcher.wait();
                }
                attemptLock();
            }
        }
    }

    /**
     * 释放锁
     *
     * @throws KeeperException
     * @throws InterruptedException
     */
    public void release() throws KeeperException, InterruptedException {
        // 删除节点
        zooKeeper.delete(lockPath, -1);
        zooKeeper.close();
        System.out.println("锁释放了");
    }

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
        PracticeDemo lock = new PracticeDemo();
        // 尝试获取锁
        lock.createLock();
        lock.attemptLock();
    }
}
